package lib

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	crypt_rand "crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/oklog/ulid"
	"io/ioutil"
	"math/big"
	math_rand "math/rand"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"
)

// get 请求
func HttpGet(uri string) ([]byte, error) {
	response, err := http.Get(uri)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
	}

	return ioutil.ReadAll(response.Body)
}

// post 请求方式2
func HttpPostValues(uri string, values url.Values) ([]byte, error) {
	response, err := http.PostForm(uri, values)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("http post error : uri=%v , statusCode=%v", uri, response.StatusCode)
	}

	return ioutil.ReadAll(response.Body)
}

// 内部签名
func ComGenSign(signData map[string]interface{}, signKey string) string {
	return GenSign(signData, signKey, "_key")
}

func GenSign(signData map[string]interface{}, signKey string, keyVal string) (sign string) {
	//排序
	var keys []string
	for k := range signData {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	//拼凑
	trimStr := "'\t\n\r \v"
	var tmpStr string
	for _, k := range keys {
		value := fmt.Sprintf("%v", signData[k])
		if signData[k] != "" {
			tmpStr += k + "=" + strings.Trim(value, trimStr) + "&"
		}
	}

	tmpStr += keyVal + "=" + signKey

	sign = strings.ToUpper(Md5(tmpStr))

	return
}

// md5加密
func Md5(str string) string {
	data := []byte(str)
	return fmt.Sprintf("%x", md5.Sum(data))
}

func PKCS5Padding(cipherText []byte, blockSize int) []byte {
	padding := blockSize - len(cipherText)%blockSize
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(cipherText, padText...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unPadding := int(origData[length-1])
	return origData[:(length - unPadding)]
}

// AES 加密
func AesEncrypt(origData, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	blockSize := block.BlockSize()
	origData = PKCS5Padding(origData, blockSize)
	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
	crypted := make([]byte, len(origData))
	blockMode.CryptBlocks(crypted, origData)
	return crypted, nil
}

// AES解密
func AesDecrypt(crypted, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

// AES加密之后的字符串
func AesEncryptString(str string, key string) (string, error) {
	encrypt, err := AesEncrypt([]byte(str), []byte(key))
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(encrypt), nil
}

// AES解密后的字符串
func AesDecryptString(str string, key string) (string, error) {
	bytesPass, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return "", err
	}

	decrypt, err := AesDecrypt(bytesPass, []byte(key))
	if err != nil {
		return "", err
	}
	return string(decrypt), nil
}

// Map 转 URL Query String
func Map2UrlQueryString(valueMap map[string]interface{}) string {
	values := url.Values{}

	for k, v := range valueMap {
		values.Set(k, v.(string))
	}

	return values.Encode()
}

// Json转Map
func Json2Map(jsonStr string) (map[string]interface{}, error) {

	valueMap := make(map[string]interface{})

	err := json.Unmarshal([]byte(jsonStr), &valueMap)
	if err != nil {
		return nil, err
	}

	return valueMap, nil
}

/*
*
检查目录是否存在，不存在则递归创建
*/
func MkDir(dirPath string) error {
	_, err := os.Stat(dirPath)
	if err == nil {
		return nil
	}

	err = os.MkdirAll(dirPath, 0755)
	if err != nil {
		return err
	}

	return nil
}

/*
*
删除文件
*/
func DeleteFile(filePath string) {
	_, err := os.Stat(filePath)

	if err != nil {
		return
	}

	os.Remove(filePath)
}

/*
*
判断文件或文件夹是否存在
*/
func CreateDir(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		return nil
	}
	if os.IsNotExist(err) {
		err = os.Mkdir(dir, 0755)
		if err == nil {
			return nil
		}
		return err
	}
	return err
}

/**
 *  @Author yang
 *  @Description 生成随机字符串
**/
func GetRandomString(len int) string {
	var container string
	var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
	b := bytes.NewBufferString(str)
	length := b.Len()
	bigInt := big.NewInt(int64(length))
	for i := 0; i < len; i++ {
		randomInt, _ := crypt_rand.Int(crypt_rand.Reader, bigInt)
		container += string(str[randomInt.Int64()])
	}
	return container
}

/**
 *  @Author yang
 *  @Description struct转换post参数
**/
func StructToMap(i interface{}) (values url.Values) {
	values = url.Values{}
	iVal := reflect.ValueOf(i).Elem()
	typ := iVal.Type()
	for i := 0; i < iVal.NumField(); i++ {
		f := iVal.Field(i)
		// You ca use tags here...
		// tag := typ.Field(i).Tag.Get("json")
		// Convert each type into a string for the url.Values string map
		var v string
		switch f.Interface().(type) {
		case int, int8, int16, int32, int64:
			v = strconv.FormatInt(f.Int(), 10)
		case uint, uint8, uint16, uint32, uint64:
			v = strconv.FormatUint(f.Uint(), 10)
		case float32:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 32)
		case float64:
			v = strconv.FormatFloat(f.Float(), 'f', 4, 64)
		case []byte:
			v = string(f.Bytes())
		case string:
			v = f.String()
		}
		// values.Set(typ.Field(i).Name, v)
		values.Set(typ.Field(i).Tag.Get("json"), v)
	}
	return
}

// GenerateID
/**
 *  @Author yang
 *  @Description 生成唯一消息ID
**/
func GenerateID() string {
	t := time.Now().UTC()
	entropy := math_rand.New(math_rand.NewSource(t.UnixNano()))
	id := ulid.MustNew(ulid.Timestamp(t), entropy)
	// id: 01G902ZSM96WV5D5DC5WFHF8WY length: 26
	return id.String()
}
